﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using KBEngine.Export;
using KBEngine.Setting.TypeDef;

namespace KBEngineTool.Export
{
	class PythonTipsExport : PythonExport
    {
        public bool Save(string dir)
		{
            Export_Math(dir);
            Export_Type(dir);
            Export_Base_Entity(dir);
            Export_Cell_Entity(dir);
            return true;
		}

        private void Export_Math(string dir)
        {
            CodeWriter codeWriter = new CodeWriter(fileMark: "# -*- coding: utf-8 -*-");
            codeWriter.Writeln("class Vector2:");
            codeWriter.IncIndent();
            codeWriter.Writeln("x: float = ...");
            codeWriter.Writeln("y: float = ...");
            codeWriter.Writeln();
            codeWriter.Writeln("@property");
            codeWriter.Writeln("def length(self) -> float: return");
            codeWriter.Writeln("@property");
            codeWriter.Writeln("def lengthSquared(self) -> float: return");
            codeWriter.Writeln();
            codeWriter.Writeln("def cross2D(self, other: Vector2) -> float: ...");
            codeWriter.Writeln("def distSqrTo(self, other: Vector2) -> float: ...");
            codeWriter.Writeln("def distTo(self, other: Vector2) -> float: ...");
            codeWriter.Writeln("def scale(self, scale: float) -> Vector2: ...");
            codeWriter.Writeln("def dot(self, other: Vector2) -> Vector2: ...");
            codeWriter.Writeln("def normalise(self) -> None: ...");
            codeWriter.Writeln("def tuple(self, other) -> tuple: ...");
            codeWriter.Writeln("def list(self, other) -> list: ...");
            codeWriter.Writeln("def set(self, *args) -> None: ...");
            codeWriter.DecIndent();
            codeWriter.Writeln();
            codeWriter.Writeln();

            codeWriter.Writeln("class Vector3:");
            codeWriter.IncIndent();
            codeWriter.Writeln("x: float = ...");
            codeWriter.Writeln("y: float = ...");
            codeWriter.Writeln("z: float = ...");
            codeWriter.Writeln();
            codeWriter.Writeln("@property");
            codeWriter.Writeln("def length(self) -> float: return");
            codeWriter.Writeln("@property");
            codeWriter.Writeln("def lengthSquared(self) -> float: return");
            codeWriter.Writeln();
            codeWriter.Writeln("def cross2D(self, other: Vector3) -> float: ...");
            codeWriter.Writeln("def distSqrTo(self, other: Vector3) -> float: ...");
            codeWriter.Writeln("def distTo(self, other: Vector3) -> float: ...");
            codeWriter.Writeln("def dot(self, other: Vector3) -> float: ...");
            codeWriter.Writeln("def flatDistSqrTo(self, other: Vector3) -> float: ...");
            codeWriter.Writeln("def flatDistTo(self, other: Vector3) -> float: ...");
            codeWriter.Writeln("def normalise(self) -> None: ...");
            codeWriter.Writeln("def scale(self, scale: float): ...");
            codeWriter.Writeln("def list(self, other) -> list: ...");
            codeWriter.Writeln("def tuple(self, other) -> tuple: ...");
            codeWriter.Writeln("def set(self, *args) -> None: ...");
            codeWriter.DecIndent();
            codeWriter.Writeln();
            codeWriter.Writeln();

            codeWriter.Writeln("class Vector4:");
            codeWriter.IncIndent();
            codeWriter.Writeln("x: float = ...");
            codeWriter.Writeln("y: float = ...");
            codeWriter.Writeln("z: float = ...");
            codeWriter.Writeln("w: float = ...");
            codeWriter.Writeln();
            codeWriter.Writeln("@property");
            codeWriter.Writeln("def length(self) -> float: return");
            codeWriter.Writeln("@property");
            codeWriter.Writeln("def lengthSquared(self) -> float: return");
            codeWriter.Writeln();
            codeWriter.Writeln("def distTo(self, other: Vector4) -> float: ...");
            codeWriter.Writeln("def distSqrTo(self, other: Vector4) -> float: ...");
            codeWriter.Writeln("def scale(self, scale: float) -> float: ...");
            codeWriter.Writeln("def dot(self, other: Vector4) -> float: ...");
            codeWriter.Writeln("def normalise(self) -> None: ...");
            codeWriter.Writeln("def tuple(self) -> tuple: ...");
            codeWriter.Writeln("def list(self) -> list: ...");
            codeWriter.Writeln("def set(self, *args) -> None: ...");
            codeWriter.Save(Path.Combine(dir, "baseapp/Math.pyi"));
            codeWriter.Save(Path.Combine(dir, "cellapp/Math.pyi"));
        }
        private void Export_Type(string dir)
        {
            CodeWriter codeWriter = new CodeWriter(fileMark: "# -*- coding: utf-8 -*-");
            codeWriter.Writeln("from typing import *");
            codeWriter.Writeln("from Math import *");
            foreach (var item in EntityDef.AllTypes)
            {
                var userType = item as UserType;
                if (userType == null)
                {
                    codeWriter.Writeln();
                    codeWriter.Writeln($"{item.TypeName}: TypeAlias = {GetTypeName(item, true)}");
                }
                else
                {
                    codeWriter.Writeln();
                    codeWriter.Writeln();
                    codeWriter.Writeln($"class {userType.TypeName}(dict):");
                    codeWriter.IncIndent();
                    if (userType.Properties.Count > 0)
                    {
                        codeWriter.Writeln("def __init__(self):");
                        codeWriter.IncIndent();
                        codeWriter.Writeln("dict.__init__({})");
                        foreach (var proper in userType.Properties)
                        {
                            codeWriter.Writeln($"self.{proper.FieldName}: {GetTypeName(proper.Type, false)}");
                        }
                        codeWriter.DecIndent();
                    }
                    codeWriter.DecIndent();
                }
            }
            codeWriter.Writeln();
            codeWriter.Save(Path.Combine(dir, "baseapp/UserTypes.py"));
            codeWriter.Save(Path.Combine(dir, "cellapp/UserTypes.py"));
        }

        private void Export_Base_Entity(string dir)
        {
            CodeWriter codeWriter = new CodeWriter(fileMark: "# -*- coding: utf-8 -*-");
            foreach (var entityDef in EntityDef.AllEntityDefs)
            {
                var baseInterfaces = entityDef.Value.Interfaces.FindAll(item => item.BaseMethods.Count > 0 || item.Properties.Exists(pro => (pro.Flags & (Flags.BASE | Flags.BASE_AND_CLIENT)) != 0));
                var baseProperties = entityDef.Value.Properties.FindAll(item => (item.Flags & (Flags.BASE | Flags.BASE_AND_CLIENT)) != 0);
                codeWriter.Writeln("from UserTypes import *");
                codeWriter.Writeln("from typing import *");
                foreach (var item in baseInterfaces)
                {
                    codeWriter.Writeln($"from {item.Name} import {item.Name}");
                }
                string baseClass = string.Join(", ", baseInterfaces.ConvertAll(item => item.Name));
                if (baseClass.Length > 0)
                    baseClass = "(" + baseClass + ")";
                codeWriter.Writeln();
                codeWriter.Writeln($"class {entityDef.Key}{baseClass}:");
                codeWriter.IncIndent();

                if (baseProperties.Count > 0)
                {
                    codeWriter.Writeln("def __init__(self):");
                    codeWriter.IncIndent();
                    if (baseInterfaces.Count > 0)
                        codeWriter.Writeln("super().__init__()");
                    foreach (var proper in baseProperties)
                    {
                        codeWriter.Writeln($"self.{proper.Name}: {GetTypeName(proper.Type, false)} = ...");
                    }
                    codeWriter.DecIndent();
                }
                codeWriter.Writeln();
                codeWriter.Writeln("# base methods");
                Export_Methods(codeWriter, entityDef.Value.BaseMethods);
                codeWriter.Writeln();
                codeWriter.Writeln("# client methods");
                Export_Methods(codeWriter, entityDef.Value.ClientMethods);
                codeWriter.Writeln("...");
                codeWriter.DecIndent();
                codeWriter.Save(Path.Combine(dir, "baseapp/" + entityDef.Key + ".pyi"));
                codeWriter.Reset();
            }
        }
        private void Export_Cell_Entity(string dir)
        {
            CodeWriter codeWriter = new CodeWriter(fileMark: "# -*- coding: utf-8 -*-");
            foreach (var entityDef in EntityDef.AllEntityDefs)
            {
                var cellInterfaces = entityDef.Value.Interfaces.FindAll(item => item.CellMethods.Count > 0 || item.Properties.Exists(pro => (pro.Flags & (Flags.BASE | Flags.BASE_AND_CLIENT)) == 0));
                var cellProperties = entityDef.Value.Properties.FindAll(item => (item.Flags & (Flags.BASE | Flags.BASE_AND_CLIENT)) == 0);
                codeWriter.Writeln("from UserTypes import *");
                codeWriter.Writeln("from typing import *");
                foreach (var item in cellInterfaces)
                {
                    codeWriter.Writeln($"from {item.Name} import {item.Name}");
                }
                string cellClass = string.Join(", ", cellInterfaces.ConvertAll(item => item.Name));
                if (cellClass.Length > 0)
                    cellClass = "(" + cellClass + ")";
                codeWriter.Writeln();
                codeWriter.Writeln($"class {entityDef.Key}{cellClass}:");
                codeWriter.IncIndent();

                if (entityDef.Value.Properties.Count > 0)
                {
                    codeWriter.Writeln("def __init__(self):");
                    codeWriter.IncIndent();
                    if (cellInterfaces.Count > 0)
                        codeWriter.Writeln("super().__init__()");
                    foreach (var proper in entityDef.Value.Properties)
                    {
                        codeWriter.Writeln($"self.{proper.Name}: {GetTypeName(proper.Type, false)} = ...");
                    }
                    codeWriter.DecIndent();
                }
                codeWriter.Writeln();
                codeWriter.Writeln("# cell methods");
                Export_Methods(codeWriter, entityDef.Value.CellMethods);
                codeWriter.Writeln("...");
                codeWriter.DecIndent();
                codeWriter.Save(Path.Combine(dir, "cellapp/" + entityDef.Key + ".pyi"));
                codeWriter.Reset();
            }
        }
        private void Export_Methods(CodeWriter codeWriter, List<MethodDef> methods)
        {
            foreach (var method in methods)
            {
                List<string> argList = new List<string>();
                argList.Add("self");
                foreach (var arg in method.Args)
                {
                    argList.Add($"{arg.TypeName.ToLower()}_{argList.Count}: {arg.TypeName}");
                }
                codeWriter.Writeln($"def {method.Name}({string.Join(", ", argList)}) -> NoReturn: ...");
            }
        }
    }
}
